#pragma once

#include "spdlog/async.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"

#if __cplusplus > 201703L
#include <format>
#else
#include "fmt/core.h"
#endif

#include <thread>
#include <atomic>

#include "01_fs.h"

class MyLog {
    spdlog::level::level_enum default_level = spdlog::level::debug;
public:
    static MyLog& instance() {
        static MyLog m_instance;
        return m_instance;
    }

    auto get_logger() const {
        return this->logger_;
    }

    void set_level(spdlog::level::level_enum level = spdlog::level::debug) {
        logger_->set_level(level);
        info_sink_->set_level(level);
        error_sink_->set_level(spdlog::level::err);
        console_sink_->set_level(level);

        if (level > spdlog::level::warn) {
            logger_->warn("Setting log level to {}. This may suppress important log messages.", spdlog::level::to_string_view(level));
        }

        logger_->info("Current log level set to: {}. Recommended levels:", spdlog::level::to_string_view(level));
        logger_->info("- debug: For detailed debugging (very verbose)");
        logger_->info("- info: For general information");
        logger_->info("- warn: For warnings and less critical issues");
        logger_->info("- error: For errors and critical issues");
    }

    void set_log_path(const std::string& path = "./logs/") {
        log_root_path = path;
        if (log_root_path.back() != '/' && log_root_path.back() != '\\') {
            log_root_path += lib_wrapper::preferred_separator();
        }
        recreate_file_sinks();
    }

    ~MyLog() {
        stop_check_thread();
        spdlog::drop_all();
    }

private:
    MyLog() {
        this->init();
    }

    void init() {
        this->init_file();
        this->init_logger();
        start_check_thread();
    }

    void init_file() {
        this->log_root_path = "./logs/";
        this->info_file_path = "info.log";
        this->error_file_path = "erro.log";
        this->rotation_h = 11;
        this->rotation_m = 59;
    }

    void init_logger() {
        spdlog::init_thread_pool(8192, 1);

        this->info_sink_ = create_daily_file_sink(this->log_root_path + this->info_file_path);
        this->error_sink_ = create_daily_file_sink(this->log_root_path + this->error_file_path);
        this->console_sink_ = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(spdlog::color_mode::automatic);

        
        this->info_sink_->set_level(default_level);
        this->error_sink_->set_level(spdlog::level::err);
        this->console_sink_->set_level(default_level);

        this->sinks_.push_back(this->info_sink_);
        this->sinks_.push_back(this->error_sink_);
        this->sinks_.push_back(this->console_sink_);

        this->logger_ = std::make_shared<spdlog::async_logger>("kslog", begin(this->sinks_), end(this->sinks_), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
        this->logger_->set_pattern(R"(%^[%l]%$ [%Y-%m-%d %H:%M:%S,%e] [Process:%P] [thread %t] - %v)");
        this->logger_->flush_on(spdlog::level::debug);
        spdlog::register_logger(this->logger_);
        spdlog::flush_every(std::chrono::seconds(5));
        this->logger_->set_level(default_level);
    }

    std::shared_ptr<spdlog::sinks::daily_file_sink_mt> create_daily_file_sink(const std::string& file_path) {
        std::string dir = lib_wrapper::parent_path(file_path);
        if (!lib_wrapper::exists(dir)) {
            lib_wrapper::create_directories(dir);
        }
        return std::make_shared<spdlog::sinks::daily_file_sink_mt>(
            file_path, this->rotation_h, this->rotation_m);
    }

    bool file_exists(const std::string& filename) {
        return lib_wrapper::exists(filename);
    }

    void check_and_rebuild_log() {
        if (!file_exists(this->log_root_path + this->info_file_path)) {
            this->info_sink_ = create_daily_file_sink(this->log_root_path + this->info_file_path);
            this->logger_->sinks()[0] = this->info_sink_;
            this->info_sink_->set_level(default_level);
            this->info_sink_->set_level(spdlog::level::err);
        }
        if (!file_exists(this->log_root_path + this->error_file_path)) {
            this->error_sink_ = create_daily_file_sink(this->log_root_path + this->error_file_path);
            this->logger_->sinks()[1] = this->error_sink_;
            this->info_sink_->set_level(default_level);
            this->error_sink_->set_level(spdlog::level::err);
        }
    }

    void recreate_file_sinks() {
        this->info_sink_ = create_daily_file_sink(this->log_root_path + this->info_file_path);
        this->error_sink_ = create_daily_file_sink(this->log_root_path + this->error_file_path);
        this->logger_->sinks()[0] = this->info_sink_;
        this->logger_->sinks()[1] = this->error_sink_;
        this->info_sink_->set_level(default_level);
        this->error_sink_->set_level(spdlog::level::err);
    }

    void start_check_thread() {
        running_ = true;
        check_thread_ = std::thread([this]() {
            while (running_) {
                std::this_thread::sleep_for(std::chrono::milliseconds(5000));
                check_and_rebuild_log();
            }
        });
    }

    void stop_check_thread() {
        running_ = false;
        if (check_thread_.joinable()) {
            check_thread_.join();
        }
    }

private:
    std::shared_ptr<spdlog::async_logger> logger_;
    std::shared_ptr<spdlog::sinks::daily_file_sink_mt> info_sink_;
    std::shared_ptr<spdlog::sinks::daily_file_sink_mt> error_sink_;
    std::shared_ptr<spdlog::sinks::stdout_color_sink_mt> console_sink_;
    std::vector<spdlog::sink_ptr> sinks_;
    std::string log_root_path;
    std::string error_file_path;
    std::string info_file_path;
    short int rotation_h{};
    short int rotation_m{};
    std::thread check_thread_;
    std::atomic<bool> running_{false};
};

#define ilog MyLog::instance().get_logger()

#if __cplusplus > 201703L
#define LOG(level, ...) ilog->level("{}:{}, {} {}", __FILE__,  __LINE__, __FUNCTION__, std::format(__VA_ARGS__))
#else
#define LOG(level, ...) ilog->level("{}:{}, {} {}", __FILE__,  __LINE__, __FUNCTION__, fmt::format(__VA_ARGS__))
#endif

// 使用示例：
// LOG(trace, "Trace message");
// LOG(debug, "Debug message");
// LOG(info, "Info message");
// LOG(warn, "Warning message");
// LOG(error, "Error message");
// LOG(critical, "Critical error message");